/*****************************************************************************
 *
 * Microchip DeviceNet Stack (Connection Object Fragmentation Source)
 *
 *****************************************************************************
 * FileName:        frag.c
 * Dependencies:    
 * Processor:       PIC18F with CAN
 * Compiler:       	C18 02.20.00 or higher
 * Linker:          MPLINK 03.40.00 or higher
 * Company:         Microchip Technology Incorporated
 *
 * Software License Agreement
 *
 * The software supplied herewith by Microchip Technology Incorporated
 * (the "Company") is intended and supplied to you, the Company's
 * customer, for use solely and exclusively with products manufactured
 * by the Company. 
 *
 * The software is owned by the Company and/or its supplier, and is 
 * protected under applicable copyright laws. All rights are reserved. 
 * Any use in violation of the foregoing restrictions may subject the 
 * user to criminal sanctions under applicable laws, as well as to 
 * civil liability for the breach of the terms and conditions of this 
 * license.
 *
 * THIS SOFTWARE IS PROVIDED IN AN "AS IS" CONDITION. NO WARRANTIES, 
 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED 
 * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
 * PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT, 
 * IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR 
 * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 *
 *
 * This file contains unacknowledged fragmentation support. Refer to 
 * section 4-4 of Volume 1 of the DeviceNet specification.
 * 
 *
 *
 * Author               Date        Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Ross Fosler			04/28/03	...	
 * 
 *****************************************************************************/




#include	"dnetdef.h"			// Global definitions file
#include 	"typedefs.h"

#if FRAGMENTATION_UNACK

#include	"conn.h"			// Internal definitions

#include	"services.h"		// Service codes
#include	"errors.h"			// Error codes
#include	"class.h"			// Class codes

#include	"dnet.h"			// Global symbols defined by the DeviceNet Obj
#include	"CAN.h"				// CAN driver






#define		_STATE_NOACK_PRODUCE_FIRST_FRAG			0
#define		_STATE_NOACK_PRODUCE_MID_FRAG			1
#define		_STATE_NOACK_PRODUCE_LAST_FRAG			2

#define		_STATE_NOACK_CONSUME_WAIT_FOR_FIRST_FRAG	0
#define		_STATE_NOACK_CONSUME_WAIT_FOR_FRAG			1


// Control flags for fragmented processing
//	<TX/RX><ACK/IO><RES><RES><RES><TXLOCK><ERR><FIN>

 
 FRAG _aFrag;
 UINT _timer;



/*********************************************************************
 * Function:        void _FragNoAckConsume(void)
 *
 * PreCondition: 	The fragment structure must be loaded with the 
 *					last known state of the calling connection.  
 *
 * Input:       	none
 *                  
 * Output:      	none
 *
 * Side Effects:    _aFrag is updated.
 *
 * Overview:      	This function handles unacknowledged reception
 *					of fragmented data.
 *
 * Note:            This function only receives data and does not 
 *					free the CAN driver to receive. It is up to the
 *					calling function to release the hardware to 
 *					receive data.
 ********************************************************************/
void _FragNoAckConsume(void)
{
	unsigned char frag, type, len;
	
	// Get the fragmentation control byte then point to the data
	frag = CANGetRxDataSIG(0); //pRxData++;
	
	// Get the fragment type
	type = frag & 0xC0;
	
	switch (type)
	{
		// First fragment received
		case 0x00:
			// Is this the first or only fragment
			if (frag == 0x3F || frag == 0)
			{
				// Store the length minus the fragment control byte
				_aFrag.rx.len = CANGetRxCnt() - 1;
				
				// Copy this fragment to the buffer
				CANGetRxDataTyp1(_aFrag.rx.pMsg);
			//	*((_MSG_DATA_IO_FRAG *)_aFrag.rx.pMsg) = *((_MSG_DATA_IO_FRAG *)pRxData);
										
				// Is this the only fragment		
				if (frag == 0x3F)
				{
					// Set the finish flag
					_aFrag.rx.fragFlags.bits.b0 = 1;
				}
				else
				{
					// Change the state
					_aFrag.rx.fragState = _STATE_NOACK_CONSUME_WAIT_FOR_FRAG;
					_aFrag.rx.oldFrag = 0;
				} 
			}
			else
			{
				// This is an error
				_aFrag.rx.fragFlags.bits.b1 = 1;
			}
			break;
		
		// Middle or last fragment
		case 0x40:
		case 0x80:
			// Has the first fragment been received
			if (_aFrag.rx.fragState == _STATE_NOACK_CONSUME_WAIT_FOR_FRAG)
			{
				// Is this the next fragment in the sequence
				if (((frag ^ (_aFrag.rx.oldFrag + 1)) & 0x3F) == 0)
				{
					// Remember the fragment control byte
					_aFrag.rx.oldFrag = frag;
		
					// Copy the length
					len = _aFrag.rx.len;
		
					// Adjust the length 
					_aFrag.rx.len += (CANGetRxCnt() - 1);
					
					// Check the size of the buffer
					if (_aFrag.rx.len <= _aFrag.rx.lenMax)
					{		
						// Copy this fragment to the buffer
						CANGetRxDataTyp1(_aFrag.rx.pMsg + len);
			//			*((_MSG_DATA_IO_FRAG *)(_aFrag.rx.pMsg + len)) = 
			//				*((_MSG_DATA_IO_FRAG *)pRxData);
							
						// Is this the last fragment
						if (type == 0x80)
						{
							// Set the finish flag
							_aFrag.rx.fragFlags.bits.b0 = 1;
						
							// Change to the beginning state
							_aFrag.rx.fragState = _STATE_NOACK_CONSUME_WAIT_FOR_FIRST_FRAG;
						}
					}
					
					// Too much data to copy to the buffer
					else
					{
						// This is an error 
						_aFrag.rx.fragFlags.bits.b1 = 1;
					
						// Go back to the first fragment
						_aFrag.rx.fragState = _STATE_NOACK_CONSUME_WAIT_FOR_FIRST_FRAG;
					}
				}
				
				// Received a fragment out of sequence
				else
				{
					// This is an error 
					_aFrag.rx.fragFlags.bits.b1 = 1;
					
					// Go back to the first fragment
					_aFrag.rx.fragState = _STATE_NOACK_CONSUME_WAIT_FOR_FIRST_FRAG;
				}
			}
			break;
		
		// Received an acknowledge fragment, which is invalid for this 
		// type of fragmentation
		case 0xC0:
			// This is an error 
			_aFrag.rx.fragFlags.bits.b1 = 1;
			
			// Go back to the first fragment
			_aFrag.rx.fragState = _STATE_NOACK_CONSUME_WAIT_FOR_FIRST_FRAG;
			break;
	}
}



/*********************************************************************
 * Function:        void _FragNoAckProduce(void)
 *
 * PreCondition:    The fragment structure must be loaded with the 
 *					last known state of the calling connection.
 *
 * Input:       	none	
 *                  
 * Output:      	none
 *
 * Side Effects:    _aFrag is updated.
 *
 * Overview:        This function handles unacknowledged transmission
 *					of fragmented data.  
 *
 * Note:            This function does not issue any requests to the 
 *					driver to send data. It is up to the calling 
 *					to issue the transmit request.
 ********************************************************************/
void _FragNoAckProduce(void)
{
	unsigned char *pTxData;
	
	// Get a pointer to the buffer
	pTxData = CANGetTxDataPtr();
		
	switch (_aFrag.tx.fragState)
	{
		// Send an unacknowledged first fragment
		case _STATE_NOACK_PRODUCE_FIRST_FRAG:
				
			// If the whole message is less than the max fragment size
			// then send the only fragment
			if (_aFrag.tx.len < 8)
			{
				// Set the fragment control byte and point to the next data location
				*pTxData = 0x3F; pTxData++;
			
				// Copy this fragment to the transmit buffer
				CANPutTxDataTyp1(_aFrag.tx.pMsg);
	//			*((_MSG_DATA_IO_FRAG *)pTxData) = 
	//				*((_MSG_DATA_IO_FRAG *)(_aFrag.tx.pMsg));
					
				// Set the length of the data to send
				CANPutTxCnt(_aFrag.tx.len + 1);
				
				// Indicate the process is finished
				_aFrag.tx.fragFlags.bits.b0 = 1;					
			}
			
			// Else send the first fragment of the whole message
			else
			{
				// Set the fragment control byte and point to the next data location
				*pTxData = _aFrag.tx.oldFrag = 0; //pTxData++;
								
				// Copy this fragment to the transmit buffer
				CANPutTxDataTyp1(_aFrag.tx.pMsg);
		//		*((_MSG_DATA_IO_FRAG *)pTxData) = 
		//			*((_MSG_DATA_IO_FRAG *)(_aFrag.tx.pMsg));
					
				// Adjust the index
				_aFrag.tx.index = 7;
				
				// Set the length of the data to send
				CANPutTxCnt(8);
								
				// Set the next state
				if ((_aFrag.tx.len - _aFrag.tx.index) > 8)
					_aFrag.tx.fragState = _STATE_NOACK_PRODUCE_MID_FRAG;
				else
					_aFrag.tx.fragState = _STATE_NOACK_PRODUCE_LAST_FRAG;
			}
			break;
			
		// Send a middle fragment
		case _STATE_NOACK_PRODUCE_MID_FRAG:
		
			// Set the fragment control byte and point to the next data location
			*pTxData = _aFrag.tx.oldFrag = ((_aFrag.tx.oldFrag + 1) & 0x3F) | 0x40; 
		//	pTxData++;
		
			// Copy this fragment to the transmit buffer
			CANPutTxDataTyp1(_aFrag.tx.pMsg + _aFrag.tx.index);
	//		*((_MSG_DATA_IO_FRAG *)pTxData) = 
	//			*((_MSG_DATA_IO_FRAG *)(_aFrag.tx.pMsg + _aFrag.tx.index));
				
			// Adjust the index
			_aFrag.tx.index += 7;
			
			// Set the length of the data to send
			CANPutTxCnt(8);
						
			// Set the next state
			if ((_aFrag.tx.len - _aFrag.tx.index) > 8)
				_aFrag.tx.fragState = _STATE_NOACK_PRODUCE_MID_FRAG;
			else
				_aFrag.tx.fragState = _STATE_NOACK_PRODUCE_LAST_FRAG;
		
			break;
			
		// Send the last fragment
		case _STATE_NOACK_PRODUCE_LAST_FRAG:
		
			// Set the fragment control byte and point to the next data location
			*pTxData = _aFrag.tx.oldFrag = ((_aFrag.tx.oldFrag + 1) & 0x3F) | 0x80; 
	//		pTxData++;
		
			// Copy this fragment to the transmit buffer
			CANPutTxDataTyp1(_aFrag.tx.pMsg + _aFrag.tx.index);
	//		*((_MSG_DATA_IO_FRAG *)pTxData) = 
	//			*((_MSG_DATA_IO_FRAG *)(_aFrag.tx.pMsg + _aFrag.tx.index));
			
			// Set the length of the data to send
			CANPutTxCnt(_aFrag.tx.len - _aFrag.tx.index + 1);
			
			// Indicate the process is finished
			_aFrag.tx.fragFlags.bits.b0 = 1;	
					
		// Fallout state, should never get here except through the last fragment
		default:
			_aFrag.tx.fragState = _STATE_NOACK_PRODUCE_FIRST_FRAG;
	}
}




#endif

	  
	  
	  
	  
	  
	  
	  
	  
	  
	  
